﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="With_HashMap.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the HashMapTests_When_Creating type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Assets;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Collections;
    using Testing;

    public class With_HashMap
    {
        protected const int NonPrimeCapacity = 12;

        protected readonly IEqualityComparer<object> AsStringEqualityComparer = new AsStringEqualityComparer();

        protected HashMap<int, int> SystemUnderTest { get; set; }

        [TestClass]
        public class When_creating_with_default_constructur : With_HashMap
        {
            [TestInitialize]
            public void Given()
            {
                SystemUnderTest = new HashMap<int, int>();
            }

            [TestMethod]
            public void Should_intialize_with_zero_capacity()
            {
                Assert.AreEqual(0, SystemUnderTest.Capacity);
            }

            [TestMethod]
            public void Should_initialize_with_default_load_factor()
            {
                Assert.AreEqual(HashMap<int, int>.DefaultLoadFactor, SystemUnderTest.LoadFactor);
            }

            [TestMethod]
            public void Should_initialize_with_default_equality_comparer()
            {
                Assert.AreEqual(EqualityComparer<int>.Default, SystemUnderTest.EqualityComparer);
            }

            [TestMethod]
            public void Should_initialize_count_to_zero()
            {
                Assert.AreEqual(0, SystemUnderTest.Count);
            }

            [TestMethod]
            public void Should_enumerate_correctly()
            {
                Assert.AreEqual(0, SystemUnderTest.ToArray().Length);
            }

            [TestMethod]
            public void Should_enumerate_keys_correctly()
            {
                Assert.AreEqual(0, SystemUnderTest.Keys.ToArray().Length);
            }

            [TestMethod]
            public void Should_enumerate_values_correctly()
            {
                Assert.AreEqual(0, SystemUnderTest.Values.ToArray().Length);
            }
        }

        [TestClass]
        public class When_creating_with_valid_capacity : With_HashMap
        {
            [TestInitialize]
            public void Given()
            {
                SystemUnderTest = new HashMap<int, int>(NonPrimeCapacity);
            }

            [TestMethod]
            public void Should_initialize_using_next_prime_after_capacity()
            {
                Assert.AreEqual(Primes.NextPrime(NonPrimeCapacity), SystemUnderTest.Capacity);
            }

            [TestMethod]
            public void Should_initialize_with_default_load_factor()
            {
                Assert.AreEqual(HashMap<int, int>.DefaultLoadFactor, SystemUnderTest.LoadFactor);
            }

            [TestMethod]
            public void Should_initialize_with_default_equality_comparer()
            {
                Assert.AreEqual(EqualityComparer<int>.Default, SystemUnderTest.EqualityComparer);
            }

            [TestMethod]
            public void Should_initialize_count_to_zero()
            {
                Assert.AreEqual(0, SystemUnderTest.Count);
            }
        }

        [TestClass]
        public class When_creating_with_valid_capacity_and_load_factor : With_HashMap
        {
            [TestInitialize]
            public void Given()
            {
                SystemUnderTest = new HashMap<int, int>(NonPrimeCapacity, 0.6);
            }

            [TestMethod]
            public void Should_initialize_using_next_prime_after_capacity()
            {
                Assert.AreEqual(Primes.NextPrime(NonPrimeCapacity), SystemUnderTest.Capacity);
            }

            [TestMethod]
            public void Should_use_default_equality_comparer()
            {
                Assert.AreEqual(EqualityComparer<int>.Default, SystemUnderTest.EqualityComparer);
            }

            [TestMethod]
            public void Should_initialize_with_specified_load_factor()
            {
                Assert.AreEqual(0.6, SystemUnderTest.LoadFactor);
            }

            [TestMethod]
            public void Should_initialize_count_to_zero()
            {
                Assert.AreEqual(0, SystemUnderTest.Count);
            }
        }

        [TestClass]
        public class When_creating_with_valid_capacity_and_load_factor_and_equality_comparer : With_HashMap
        {
            public class TestEqualityComparer : IEqualityComparer<int>
            {
                public bool Equals(int x, int y)
                {
                    return x == y;
                }

                public int GetHashCode(int obj)
                {
                    return obj;
                }

                public static readonly TestEqualityComparer Instance = new TestEqualityComparer();
            }

            [TestInitialize]
            public void Given()
            {
                SystemUnderTest = new HashMap<int, int>(NonPrimeCapacity, 0.6, TestEqualityComparer.Instance);
            }

            [TestMethod]
            public void Should_initialize_using_next_prime_after_capacity()
            {
                Assert.AreEqual(Primes.NextPrime(NonPrimeCapacity), SystemUnderTest.Capacity);
            }

            [TestMethod]
            public void Should_use_specified_equality_comparer()
            {
                Assert.AreSame(TestEqualityComparer.Instance, SystemUnderTest.EqualityComparer);
            }

            [TestMethod]
            public void Should_initialize_load_factor()
            {
                Assert.AreEqual(0.6, SystemUnderTest.LoadFactor);
            }

            [TestMethod]
            public void Should_initialize_count_to_zero()
            {
                Assert.AreEqual(0, SystemUnderTest.Count);
            }
        }
        
        [TestClass]
        public class When_creating_from_existing_dictionary : With_HashMap
        {
            [TestInitialize]
            public void Given()
            {
                SystemUnderTest = new HashMap<int, int>(new Dictionary<int, int> { { 1, 2 }, { 3, 4 }, { 5, 6 } });
            }

            [TestMethod]
            public void When_should_initialize_with_items_copied()
            {
                Assert.AreEqual(3, SystemUnderTest.Count);
                Assert.AreEqual(2, SystemUnderTest[1]);
                Assert.AreEqual(4, SystemUnderTest[3]);
                Assert.AreEqual(6, SystemUnderTest[5]);
            }
        }

        [TestClass]
        public class When_new_key_added : With_HashMap
        {
            protected const int Key = 1;
            protected const int Value = 2;

            [TestInitialize]
            public virtual void Given()
            {
                SystemUnderTest = new HashMap<int, int>();
                SystemUnderTest.Add(Key, Value);
            }
            
            [TestMethod]
            public void Should_be_accessible_through_indexer()
            {
                Assert.AreEqual(Value, SystemUnderTest[Key]);
            }

            [TestMethod]
            public void Should_increment_count()
            {
                Assert.AreEqual(1, SystemUnderTest.Count);
            }

            [TestMethod]
            public void Should_be_able_to_access_key_through_keys_collection()
            {
                Assert.IsNotNull(SystemUnderTest.Keys.FirstOrDefault(x => x == Key));
            }

            [TestMethod]
            public void Should_be_able_to_access_value_through_values_collection()
            {
                Assert.IsNotNull(SystemUnderTest.Values.FirstOrDefault(x => x == Value));
            }

            [TestMethod]
            public void Should_be_able_to_access_key_value_pair()
            {
                Assert.AreEqual(new KeyValuePair<int, int>(Key, Value), SystemUnderTest.FirstOrDefault());
            }
        }
        
        [TestClass]
        public class When_new_key_set : When_new_key_added
        {
            [TestInitialize]
            public override void Given()
            {
                SystemUnderTest = new HashMap<int, int>();
                SystemUnderTest[Key] = Value;
            }
        }

        [TestClass]
        public class When_duplicate_key_set : With_HashMap
        {
            public const int Key = 1;
            public const int InitialValue = 2;
            public const int ReplacementValue = 2;

            [TestInitialize]
            public void Given()
            {
                SystemUnderTest = new HashMap<int, int>();
                SystemUnderTest[Key] = InitialValue;
                SystemUnderTest[Key] = ReplacementValue;
            }

            [TestMethod]
            public void Should_replace_value()
            {
                Assert.AreEqual(ReplacementValue, SystemUnderTest[Key]);
            }
        }

        [TestClass]
        public class When_duplicate_key_added : With_HashMap
        {
            public const int Key = 1;
            public const int Value = 2;
            
            [TestInitialize]
            public void Given()
            {
                SystemUnderTest = new HashMap<int, int>();
                SystemUnderTest[Key] = Value;
            }

            [TestMethod]
            public void Should_throw_argument_exception()
            {
                AssertException.Throws<ArgumentException>(() => SystemUnderTest.Add(Key, Value));
            }
        }
    }
}
